﻿using Rpc.Abstractions;
using Rpc.Abstractions.Exceptions;
using Rpc.Remoting;
using Rpc.Abstractions.CircuitBreaker;
using Common.MefHelp;
using Common.SelectService.Select;
using Common.SelectService;

namespace Rpc.DynamicProxy.Interceptor
{
    public sealed class ClientDefaultInterceptor : InterceptorAbstract
    {
        //private readonly ILoadBalancerManager _loadBalancerManager;
        private readonly IClientFactory _clientFactory;
        private readonly ICircuitBreaker _circuitBreaker;
        private readonly RpcSettings _rpcSettings;

        public ClientDefaultInterceptor(IClientFactory clientFactory, ICircuitBreaker circuitBreaker, RpcSettings rpcSettings)
        {
            _clientFactory = clientFactory;
            _circuitBreaker = circuitBreaker;
            _rpcSettings = rpcSettings;
        }

        public override async Task<IServiceResult> Intercept(IInterceptorContext context)
        {
            if (!(context is InterceptorContext ctx)) throw new ArgumentNullException(nameof(context));
            //No circuit breaker
            if (_rpcSettings.CircuitBreakerOptions == null)
            {
                return await Exec(ctx.ServiceName, ctx.ServiceRoute, ctx.Args, ctx.Meta);
            }
            //Circuit breaker
            return await _circuitBreaker.ExecuteAsync(ctx.ServiceRoute,
                async () => await Exec(ctx.ServiceName, ctx.ServiceRoute, ctx.Args, ctx.Meta), ctx.ReturnType);
        }

        private async Task<IServiceResult> Exec(string serviceName, string route, object[] args, Dictionary<string, string> meta)
        {
            var selectServiceType = SelectServiceConfig.Instance != null ? SelectServiceConfig.Instance.SelectServiceType.ToLower() : "consul";
            var url = await Mef.GetService<ISelectService>(selectServiceType).GetHostAndPortAsync(serviceName);//"http://192.168.102.90:5161";
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new RemoteInvokeException(route, $"发现服务为空：{serviceName}", RemotingStatus.Error);
            }
            var uri = new Uri(url);
            var client = await _clientFactory.CreateClientAsync(serviceName, uri.Host, uri.Port);
            var result = await client.SendAsync(new InvokeMessage(route, args, meta));
            if (result.Status != RemotingStatus.Ok)
                throw new RemoteInvokeException(route, result.Result?.ToString() ?? string.Empty, result.Status);
            return result;
        }
    }
}
